using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Hardware;
using System.Threading;
using System.Threading.Tasks;

namespace CoachingOrganizer.Android.Hardware
{
    public class CameraVideoViewWrapper : IDisposable
    {
        private class CallbackLoop : Java.Lang.Object, ISurfaceHolderCallback
        {
            private readonly Camera _camera;

            public Camera Camera
            {
                get { return _camera; }
            }

            public CallbackLoop(Camera camera)
            {
                _camera = camera;
            }

            public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
            {
                _camera.SetPreviewDisplay(holder);
                _camera.StartPreview();
            }

            public void SurfaceCreated(ISurfaceHolder holder)
            {
                _camera.SetPreviewDisplay(holder);
            }

            public void SurfaceDestroyed(ISurfaceHolder holder)
            {
                _camera.StopPreview();
            }

            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                _camera.Release();
            }
        }

        private class TakePictureListener : Java.Lang.Object, Camera.IShutterCallback, Camera.IPictureCallback
        {
            private readonly EventWaitHandle _waitPicture = new EventWaitHandle(true, EventResetMode.ManualReset);
            private byte[] _lastPicture;

            public event EventHandler ShutterUsed;

            public void BeginWaitPicture(Action<byte[]> resultCallback)
            {
                _waitPicture.Reset();
                Task.Factory.StartNew(() =>
                {
                    _waitPicture.WaitOne();
                    if (_lastPicture != null)
                        resultCallback(_lastPicture);
                });
            }

            public void OnShutter()
            {
                OnShutterUsed();
            }

            public void OnPictureTaken(byte[] data, Camera camera)
            {
                if (data == null)
                    return;
                global::Android.Graphics.BitmapFactory.Options estimationOptions = new global::Android.Graphics.BitmapFactory.Options();
                estimationOptions.InJustDecodeBounds = true;
                int originalWidth, originalHeight;
                using (global::Android.Graphics.Bitmap estaimationEmotionPicture = global:: Android.Graphics.BitmapFactory.DecodeByteArray(data, 0, data.Length, estimationOptions)) ;
                {
                    originalWidth = estimationOptions.OutWidth;
                    originalHeight = estimationOptions.OutHeight;
                }
                int scaleFactor = (int)Math.Ceiling(Math.Max(originalWidth / 400.0, originalHeight / 400.0));
                global::Android.Graphics.BitmapFactory.Options readOptions = new global::Android.Graphics.BitmapFactory.Options()
                 {
                     InSampleSize = scaleFactor,
                     InPreferredConfig = global:: Android.Graphics.Bitmap.Config.Rgb565
                 };
                using (global::Android.Graphics.Bitmap emotionPicture = global::  Android.Graphics.BitmapFactory.DecodeByteArray(data, 0, data.Length, readOptions))
                {
                    using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
                    {
                        if (emotionPicture.Compress(global:: Android.Graphics.Bitmap.CompressFormat.Jpeg, 20, stream))
                            _lastPicture = stream.ToArray();
                    }
                }
                _waitPicture.Set();
            }

            protected virtual void OnShutterUsed()
            {
                EventHandler handler = ShutterUsed;
                if (handler != null)
                    handler(this, EventArgs.Empty);
            }
        }

        private readonly VideoView _videoView;
        private int _currentCameraIndex;
        private CallbackLoop _currentLoop;

        public int CurrentCameraIndex
        {
            get { return _currentCameraIndex; }
            set
            {
                _currentCameraIndex = value;
                UpdateCurrentCamera();
            }
        }

        public event EventHandler SnapshotFailed;

        public CameraVideoViewWrapper(VideoView videoView)
        {
            _videoView = videoView;
            UpdateCurrentCamera();
        }

        public void Dispose()
        {
            ReleaseCurrentCameraResources();
        }

        //public byte[] SnapshotToPng()
        //{
        //    byte[] pictureBytes = null;
        //    using (Android.Graphics.Bitmap emotionPicture = _videoView.GetDrawingCache(true))
        //    {
        //        if (emotionPicture == null)
        //        {
        //            OnSnapshotFailed();
        //            return null;
        //        }
        //        using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
        //            if (emotionPicture.Compress(Android.Graphics.Bitmap.CompressFormat.Png, 20, stream))
        //                pictureBytes = stream.ToArray();
        //    }
        //    return pictureBytes;
        //}

        public void BeginSnapshotToJpeg(Action<byte[]> gotJpegCallback)
        {
            CallbackLoop currentLoop = _currentLoop;
            if (currentLoop == null)
            {
                OnSnapshotFailed();
                return;
            }
            TakePictureListener takePictureListener = new TakePictureListener();
            takePictureListener.BeginWaitPicture(data =>
            {
                takePictureListener.Dispose();
                gotJpegCallback(data);
            });
            currentLoop.Camera.TakePicture(takePictureListener, null, takePictureListener);
        }

        private void UpdateCurrentCamera()
        {
            ReleaseCurrentCameraResources();

            Camera camera = Camera.Open(_currentCameraIndex);
            camera.AutoFocus(null);
            _currentLoop = new CallbackLoop(camera);
            _videoView.Holder.SetType(SurfaceType.PushBuffers);
            _videoView.Holder.AddCallback(_currentLoop);
            _videoView.Start();
        }

        private void ReleaseCurrentCameraResources()
        {
            if (_currentLoop != null)
                _currentLoop.Dispose();
        }

        protected virtual void OnSnapshotFailed()
        {
            EventHandler handler = SnapshotFailed;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }
    }
}